package run.microservice.stock.aspects;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MissingRequestValueException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;
import run.microservice.stock.response.BusinessException;
import run.microservice.stock.response.Result;
import run.microservice.stock.response.ResultEnum;

import java.sql.SQLException;

/**
 * 全局异常处理
 * @author Lundy
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 注解@RequestBody用于接收body参数，对@RequestBody参数的校验失败会抛出MethodArgumentNotValidException
     * 注解@ModelAttribute用于接收URL参数，对@ModelAttribute参数的校验失败会抛出BindException
     * 但是MethodArgumentNotValidException继承了BindException，所以只处理BindException即可
     * @param bindException 参数绑定异常
     * @return
     */
    @ExceptionHandler(BindException.class)
    public Object handleBindException(BindException bindException) {
        StringBuilder codeBuilder = new StringBuilder();
        StringBuilder msgBuilder = new StringBuilder();

        for (FieldError fieldError : bindException.getFieldErrors()) {
            String code = fieldError.getDefaultMessage();
            codeBuilder.append(code).append("，");
            ResultEnum resultEnum = ResultEnum.getInstance(code);
            if (resultEnum == null) {
                msgBuilder.append(code).append("；");
                continue;
            }
            msgBuilder.append(resultEnum.getMsg()).append("；");
        }

        Result result = Result.error(ResultEnum.PARAM_ERROR);
        result.setMsg(msgBuilder.toString());
        log.error("参数校验：{}\n    异常信息：{}\n    at {}", codeBuilder, result.getMsg(), bindException.getMessage());
        return result;
    }

    /**
     * 对@RequestParam参数的校验失败会抛出MethodArgumentNotValidException
     * get请求传参优先推荐使用@ModelAttribute注解+实体入参
     * @param paramValidException @RequestParam参数校验异常
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Object handleConstraintViolationException(ConstraintViolationException paramValidException) {
        StringBuilder codeBuilder = new StringBuilder();
        StringBuilder msgBuilder = new StringBuilder();

        for (ConstraintViolation<?> constraintViolation : paramValidException.getConstraintViolations()) {
            String code = constraintViolation.getMessage();
            codeBuilder.append(code).append("，");
            ResultEnum resultEnum = ResultEnum.getInstance(code);
            if (resultEnum == null) {
                msgBuilder.append(code).append("；");
                continue;
            }
            msgBuilder.append(resultEnum.getMsg()).append("；");
        }

        Result result = Result.error(ResultEnum.PARAM_ERROR);
        result.setMsg(msgBuilder.toString());
        log.error("参数校验：{}\n    异常信息：{}\n    at {}", codeBuilder, result.getMsg(), paramValidException.getMessage());
        return result;
    }
    @ExceptionHandler(MissingRequestValueException.class)
    public Object handleMissingRequestValueException(MissingRequestValueException missParamException) {
        String message = missParamException.getMessage();
        Result result = Result.error(ResultEnum.PARAM_ERROR);
        result.setMsg(message);
        log.error("@RequestParam参数丢失：{}", message);
        return result;
    }

    /**
     * 业务异常处理
     * @param businessException 自定义业务异常
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    public Object handleBusinessException(BusinessException businessException) {
        String stackTraceInfo = this.getFirstLinetStackTraceInfo(businessException.getStackTrace());
        Result result = Result.error(businessException.getResultEnum(), businessException.getArgs());
        log.error("业务异常：{}\n    异常信息：{}\n    at {}", result.getCode(), result.getMsg(), stackTraceInfo);
        return result;
    }
    
    @ExceptionHandler(SQLException.class)
    public Object handleSQLException(SQLException sqlException) {
        Result result = Result.error(ResultEnum.SQL_ERROR);
        result.setMsg(sqlException.getMessage());
        log.error("SQL异常", sqlException);
        return result;
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public Object handleDuplicateKeyException(DuplicateKeyException dkException) {
        Result result = Result.error(ResultEnum.DUPLICATE_KEY);
        log.error("重复Key异常", dkException);
        return result;
    }
    
    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e) {
        log.error("系统错误", e);
        String msg = e.getMessage() == null ? e.toString() : e.getMessage();
        return Result.error(ResultEnum.ERROR, msg);
    }

    /**
     * 取第1行堆栈信息；目的是要知道是哪个类抛出异常的
     * @param elements
     * @return
     */
    private String getFirstLinetStackTraceInfo(StackTraceElement[] elements) {
        if (elements != null && elements.length > 0) {
            return elements[0].toString();
        }
        return "";
    }

}
